package cn.bincker.mybatis.encrypt;

import cn.bincker.mybatis.encrypt.converter.EncryptConvertRegister;
import cn.bincker.mybatis.encrypt.converter.impl.DefaultEncryptConvertRegister;
import cn.bincker.mybatis.encrypt.core.*;
import cn.bincker.mybatis.encrypt.core.impl.AesAndSha256Encryptor;
import cn.bincker.mybatis.encrypt.core.impl.EncryptCacheManagerImpl;
import cn.bincker.mybatis.encrypt.entity.SecurityProperty;
import cn.bincker.mybatis.encrypt.reflection.EncryptReflectorFactory;
import cn.bincker.mybatis.encrypt.reflection.factory.EncryptObjectFactory;
import cn.bincker.mybatis.encrypt.type.EncryptTypeHandlerConfigurator;
import cn.bincker.mybatis.encrypt.wrapper.EncryptObjectWrapperFactory;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;

import javax.crypto.spec.SecretKeySpec;
import java.util.List;

@Slf4j
public class MybatisEncryptAutoConfiguration {
    private final MybatisEncryptConfigurer mybatisEncryptConfigurer;

    public MybatisEncryptAutoConfiguration(ObjectProvider<MybatisEncryptConfigurer> mybatisEncryptConfigurer) {
        this.mybatisEncryptConfigurer = mybatisEncryptConfigurer.getIfAvailable(()->new MybatisEncryptConfigurer() {
            @Override
            public Encryptor getEncryptor() {
                return new AesAndSha256Encryptor();
            }

            @Override
            public EncryptKeyProvider getEncryptKeyProvider() {
                return new EncryptKeyProvider() {
                    @Override
                    public Object getKey(SecurityProperty property, Object target) {
                        return new SecretKeySpec("hello_bincker_12".getBytes(), "AES");
                    }

                    @Override
                    public Object getSalt(SecurityProperty property, Object target) {
                        return "bincker".getBytes();
                    }
                };
            }
        });
    }

    @Bean
    @ConditionalOnMissingBean(EncryptConvertRegister.class)
    public EncryptConvertRegister defaultEncryptConvertRegister(){
        return new DefaultEncryptConvertRegister();
    }

    @Bean
    public EncryptCacheManager encryptCacheManager(List<EncryptCache<?>> encryptCaches){
        return new EncryptCacheManagerImpl(encryptCaches);
    }

    @Bean
    public EncryptExecutor defaultEncryptExecutor(
            EncryptConvertRegister convertRegister,
            EncryptCacheManager encryptCacheManager
    ){
        return mybatisEncryptConfigurer.getEncryptExecutor(convertRegister, encryptCacheManager);
    }

    @Bean
    @ConditionalOnMissingClass("com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer")
    public ConfigurationCustomizer encryptMybatisCustomizer(EncryptExecutor encryptExecutor){
        return configuration -> configure(configuration, encryptExecutor);
    }

    @Bean
    @ConditionalOnClass(com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer.class)
    public com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer encryptMybatisPlusCustomizer(EncryptExecutor encryptExecutor){
        return configuration -> configure(configuration, encryptExecutor);
    }

    private void configure(org.apache.ibatis.session.Configuration configuration, EncryptExecutor encryptExecutor){
        configuration.setObjectWrapperFactory(new EncryptObjectWrapperFactory(encryptExecutor));
        configuration.setObjectFactory(new EncryptObjectFactory());
        configuration.setReflectorFactory(new EncryptReflectorFactory());
        EncryptTypeHandlerConfigurator.configure(configuration, encryptExecutor);
    }

    @Bean
    public Encryptor defaultEncryptor(){
        return mybatisEncryptConfigurer.getEncryptor();
    }

    @Bean
    public EncryptKeyProvider temporaryKeyProvider(){
        return mybatisEncryptConfigurer.getEncryptKeyProvider();
    }
}
